﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;

using log4net;
using ConfigUtilLib;
using CommonLib;
using QueueMgrUtil;


namespace DataFetcherProcessorLib
{
    /// <summary>
    /// Purpose of this class is to Read Configuration 
    /// Start fetching data and adding to correct models 
    /// and adding a processing Job to the Queue
    /// Btw , this is singleton class 
    /// </summary>
    public class FetcherManager
    {
        private const string interfaceStr = "CommonLib.FetcherInterface"; 

        #region PRIVATE_MEMBERS_CONSTRUCTOR
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        //private static BlockReadQueue<QueueWorkItem> _processingQueue = null;
        private static CountdownEvent _countdown=null;
        private static AppConfig _appConfig = null;
        #endregion 

        /// <summary>
        /// Constrtuctor : Init lot of good Stuff.. 
        /// Please follow Documentation inside the prodecure (a.k.a method  for JJJAAAVVVAAA Guys :-) ) 
        /// </summary>
        private  FetcherManager()
        {

        }

        public static void KickOffDataFetcher(AppConfig appCfg)
        {
           //_processingQueue = QueueManager.WorkItemsQueue;
           _countdown = new CountdownEvent(appCfg.FetcherRecords.Count);
           _appConfig = appCfg;
           ThreadPool.QueueUserWorkItem(RunDatafetcherThread);
        }

        #region PRIVATE_UTILS_HELPERS

        private static void RunDatafetcherThread(Object args)
        {
            
           
            // Loop thru Configuration for all Frameworks and Fetch Data 
            foreach ( FetcherConfigBaseRec  fetcherRec in _appConfig.FetcherRecords)
            {                
                if (!ThreadPool.QueueUserWorkItem(GoFetchData, fetcherRec))
                {
                    Console.Error.WriteLine("Failed to Queue Fetcher to Thread.. {0}", fetcherRec.FetcherName);
                }
                else
                {
                    log.DebugFormat("Queued to Fetcher Thread.. :  {0}", fetcherRec.FetcherName);
                }
                
            }

            // Queue End of the work Queue Item 
            _countdown.Wait();
            QueueManager.EnqueueEndOfWork();

            /*
            QueueWorkItem endoftheWork = new QueueWorkItem(QUEUE_WORK_ITEM_TYPE.PROCESS_END, null, null);
            _processingQueue.Enqueue(endoftheWork);
             * */

        }

        /// <summary>
        /// Go Fetch Data  Add it to Processing Queue
        /// GoFetchData depends on Fetcher Plugin to fetchdata.
        /// </summary>
        /// <param name="fetchRecordObject"></param>
        
        private static void  GoFetchData( object fetchRecordObject )
        {

            var fetchRec = (FetcherConfigBaseRec)fetchRecordObject;            
            log.DebugFormat(" Fetching Data for  fetch Record :" + fetchRec.FetcherName);

            //  perform Fecthing Operation 

            if (!QueueManager.isWorkItemQueuPresent()) return;
            Object rawDataFromCrawler = null;
            Assembly reflectionAssembly = null;
            var fetcherBinRec = _appConfig.appgeneralConfig.GetFetcherBin(fetchRec.FetcherType);

            if( fetcherBinRec ==  null )
            {
                return;
            }

            try
            {
                // 1. Load Library 
                reflectionAssembly = Assembly.LoadFrom(fetcherBinRec.Dll);
            }
            catch (Exception e)
            {
                log.DebugFormat("Unable to Load DLL : {0} : {1}", fetcherBinRec.Dll, e.Message);
                return ;
            }

            if (reflectionAssembly == null) return;

            foreach (Type classType in reflectionAssembly.GetTypes())
            {
                if ((classType == null) || (classType.IsClass != true) || (classType.GetInterface(interfaceStr) == null))
                {
                    continue;
                }
                // if all good Invoke The method 
                var ibaseObject = Activator.CreateInstance(classType) as FetcherInterface;
                log.DebugFormat("......Dynamically Invoking compute() on {0} ", classType.FullName);
                // Kick Off Crawling /Fetching data  
                if (ibaseObject != null) rawDataFromCrawler = ibaseObject.crawlForData(fetchRec);
                break;  //  Let us do for only no class object 
            }



            QueueManager.EnqueueWorkItem(fetchRec, rawDataFromCrawler);

            /*
                QueueWorkItem workItemToProcess = new QueueWorkItem(QUEUE_WORK_ITEM_TYPE.PROCESS_WORK_ITEM, fetchRec, rawDataFromCrawler );
                _processingQueue.Enqueue(workItemToProcess);*/
            // Signal Back 
            _countdown.Signal();
        }



        #endregion 
    }
}
